﻿using System;
using System.Collections.Generic;
using System.Reflection;

namespace Murry.DataStore
{
    /// <summary>
    /// The one true DataStore. 
    /// </summary>
    public static class TheDataStore
    {
        /// <summary>
        /// Instance of the specific DataStore for a given interface
        /// </summary>
        /// <typeparam name="TInterface">Interface type</typeparam>
        /// <returns>DataStore instance</returns>
        private static IReadOnlyDataStore Instance<TInterface>() 
        {
            foreach (var scheme in Schemes)
            {
                if (scheme.Types.Contains(typeof(TInterface)))
                {
                    return scheme.DataStore;
                }
            }

            //// try asking each DataStore if the type is supported.
            //foreach (var scheme in Schemes)
            //{
            //    if (scheme.DataStore.IsSupported<TInterface>())
            //    {
            //        return scheme.DataStore;
            //    }
            //}

            throw new InvalidOperationException("Type not supported.");
        }

        /// <summary>
        /// Find object by ID.
        /// </summary>
        /// <typeparam name="TInterface">Interface type</typeparam>
        /// <param name="id">Object ID</param>
        /// <returns>Object</returns>
        public static TInterface FindById<TInterface>(string id) 
        {
            return Instance<TInterface>().FindById<TInterface>(id);
        }

        /// <summary>
        /// Find all objects of a given type.
        /// </summary>
        /// <typeparam name="TInterface">Interface type</typeparam>
        /// <returns>Objects</returns>
        public static IEnumerable<TInterface> FindAll<TInterface>() 
        {
            return Instance<TInterface>().FindAll<TInterface>();
        }

        /// <summary>
        /// Get a filtered list of objects.
        /// </summary>
        /// <typeparam name="TInterface">Interface type</typeparam>
        /// <param name="filter">Filter</param>
        /// <returns>Objects</returns>
        public static IEnumerable<TInterface> FindByFilter<TInterface>(object filter) 
        {
            return Instance<TInterface>().FindByFilter<TInterface>(filter);
        }

        /// <summary>
        /// Create a new object that implements the given interface.
        /// </summary>
        /// <typeparam name="TInterface">Interface type</typeparam>
        /// <returns>New entity.</returns>
        /// <exception cref="InvalidOperationException">If Create is called on a read-only DataStore.</exception>
        public static TInterface Create<TInterface>() 
            where TInterface : IPersist
        {
            var instance = Instance<TInterface>() as IDataStore;

            if (instance == null)
            {
                throw new InvalidOperationException("Cannot Create an entity with a read-only DataStore.");
            }

            return instance.Create<TInterface>();
        }

        /// <summary>
        /// Supported Schemes.
        /// </summary>
        public static readonly DataSchemes Schemes = new DataSchemes();

        /// <summary>
        /// Searches for static SupportedEntities.RegisterEntities() in a given assembly and Invokes it.
        /// </summary>
        /// <param name="assemblyPath">Assembly path</param>
        public static void RegisterEntities(string assemblyPath)
        {
            var assembly = Assembly.Load(assemblyPath);

            foreach (var module in assembly.GetModules())
            {
                foreach (var type in module.GetTypes())
                {
                    if (type.Name != "SupportedEntities") continue;

                    var method = type.GetMethod(
                        "RegisterEntities",
                        BindingFlags.Static | BindingFlags.Public,
                        null,
                        new Type[] {},
                        null);

                    if (method != null)
                    {
                        method.Invoke(null, new object[] {});
                    }
                }
            }
        }

        /// <summary>
        /// Searches for static SupportedSchemes.RegisterSchemes() in a given assembly and Invokes it.
        /// </summary>
        /// <param name="assemblyPath">Assembly path</param>
        public static void RegisterSchemes(string assemblyPath)
        {
            var assembly = Assembly.Load(assemblyPath);

            foreach (var module in assembly.GetModules())
            {
                foreach (var type in module.GetTypes())
                {
                    if (type.Name != "SupportedSchemes") continue;

                    var method = type.GetMethod(
                        "RegisterSchemes",
                        BindingFlags.Static | BindingFlags.Public,
                        null,
                        new Type[] { },
                        null);

                    if (method != null)
                    {
                        method.Invoke(null, new object[] { });
                    }
                }
            }
        }

    }
}
